// Definition of simple stack machine and simple emulator for Clang level 4
// Includes procedures, functions, parameters, arrays, concurrency.
// This version emulates one CPU time sliced between processes.
// Display machine
// P.D. Terry, Rhodes University, 1996

#ifndef STKMC_H
#define STKMC_H

#define STKMC_version     "Clang 4.0"
const int STKMC_memsize = 512;  // Limit on memory
const int STKMC_levmax = 5;     // Limit on Display
const int STKMC_headersize = 5; // Size of minimum activation record
const int STKMC_procmax = 10;   // Limit on concurrent processes

// machine instructions - order important
enum STKMC_opcodes {
  STKMC_cal, STKMC_ret, STKMC_adr, STKMC_frk, STKMC_cbg, STKMC_lit, STKMC_dsp,
  STKMC_brn, STKMC_bze, STKMC_prs, STKMC_wgt, STKMC_sig, STKMC_cnd, STKMC_nfn,
  STKMC_mst, STKMC_add, STKMC_sub, STKMC_mul, STKMC_dvd, STKMC_eql, STKMC_neq,
  STKMC_lss, STKMC_geq, STKMC_gtr, STKMC_leq, STKMC_neg, STKMC_val, STKMC_sto,
  STKMC_ind, STKMC_stk, STKMC_hlt, STKMC_inn, STKMC_prn, STKMC_nln, STKMC_nop,
  STKMC_nul
};

typedef enum {
  running, finished, badmem, baddata, nodata, divzero, badop, badind,
  badfun, badsem, deadlock
} status;
typedef int STKMC_address;
typedef int STKMC_levels;
typedef int STKMC_procindex;

class STKMC {
  public:
    int mem[STKMC_memsize];  // virtual machine memory

    void listcode(char *filename, STKMC_address codelen);
    // Lists the codelen instructions stored in mem on named output file

    void emulator(STKMC_address initpc, STKMC_address codelen,
                  STKMC_address initsp, FILE *data, FILE *results,
                  bool tracing);
    // Emulates action of the codelen instructions stored in mem, with
    // program counter initialized to initpc, stack pointer initialized to
    // initsp.  data and results are used for I/O.  Tracing at the code level
    // may be requested

    void interpret(STKMC_address codelen, STKMC_address initsp);
    // Interactively opens data and results files.  Then interprets the
    // codelen instructions stored in mem, with stack pointer initialized
    // to initsp

    STKMC_opcodes opcode(char *str);
    // Maps str to opcode, or to MC_nul if no match can be found

    STKMC();
    // Initializes stack machine

  private:
    struct processor {
      STKMC_opcodes ir;  // Instruction register
      int bp;            // Base pointer
      int sp;            // Stack pointer
      int mp;            // Mark Stack pointer
      int pc;            // Program counter
    };
    struct processrec { // Process descriptors
      STKMC_address bp, mp, sp, pc;      // Shadow registers
      STKMC_procindex next;              // Ring pointer
      STKMC_procindex queue;             // Linked, waiting on semaphore
      bool ready;                        // Process ready flag
      STKMC_address stackmax, stackmin;  // Memory limits
      int display[STKMC_levmax];         // Display registers
    };
    processor cpu;
    status ps;

    bool inbounds(int p);

    char *mnemonics[STKMC_nul+1];
    void stackdump(STKMC_address initsp, FILE *results, STKMC_address pcnow);
    void trace(FILE *results, STKMC_address pcnow);
    void postmortem(FILE *results, STKMC_address pcnow);

    int slice;
    STKMC_procindex current, nexttorun;
    processrec process[STKMC_procmax + 1];
    void swapregisters(void);
    void chooseprocess(void);
    void signal(STKMC_address semaddress);
    void wait(STKMC_address semaddress);
};

#endif /*STKMC_H*/
